//==============================================================================
//
//  This file is part of GPSTk, the GPS Toolkit.
//
//  The GPSTk is free software; you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published
//  by the Free Software Foundation; either version 3.0 of the License, or
//  any later version.
//
//  The GPSTk is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with GPSTk; if not, write to the Free Software Foundation,
//  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
//  
//  This software was developed by Applied Research Laboratories at the
//  University of Texas at Austin.
//  Copyright 2004-2020, The Board of Regents of The University of Texas System
//
//==============================================================================

//==============================================================================
//
//  This software was developed by Applied Research Laboratories at the
//  University of Texas at Austin, under contract to an agency or agencies
//  within the U.S. Department of Defense. The U.S. Government retains all
//  rights to use, duplicate, distribute, disclose, or release this software.
//
//  Pursuant to DoD Directive 523024 
//
//  DISTRIBUTION STATEMENT A: This software has been approved for public 
//                            release, distribution is unlimited.
//
//==============================================================================

/**
 * @file OrbElemStore.hpp
 * Base class for storing and/or computing position, velocity, 
 * and clock data.  This class is typically extended in order
 * to implement SV-system specific behavior.  Theoretically, 
 * it could be used to store OrbElemBase objects across multiple
 * systems, but that would entail moving all the time tags to a 
 * common system. 
 */
 
#ifndef GPSTK_ORBELEMSTORE_HPP
#define GPSTK_ORBELEMSTORE_HPP

#include <iostream>
#include <list>
#include <map>

#include "CommonTime.hpp"
#include "Exception.hpp"
#include "OrbElemBase.hpp"
#include "SatID.hpp"
#include "TimeSystem.hpp"
#include "XvtStore.hpp"

namespace gpstk
{
      /// @ingroup ephemstore 
      //@{

      /// Base class for storing and accessing an objects position, 
      /// velocity, and clock data. Also defines a simple interface to remove
      /// data that has been added.
   class OrbElemStore : public XvtStore<SatID>
   {
   public:
      
      OrbElemStore()
      throw()
      :initialTime(CommonTime::END_OF_TIME), 
         finalTime(CommonTime::BEGINNING_OF_TIME),
         timeSysForStore(TimeSystem::Any)
      {
         initialTime.setTimeSystem(timeSysForStore);
         finalTime.setTimeSystem(timeSysForStore);
         setOnlyHealthyFlag(true);
      }

         ///
         /// Notes regarding the rationalize( ) function.
         /// The timing relationships defined in IS-GPS-200 20.3.4.5 mean
         /// (1.) The end of validity of a given set of orbital elements
         /// may be determined by the beginning of transmission of a new
         /// upload.   
         /// (2.) The beginning of validity of the SECOND set of elements
         /// following and upload should be Toe-(0.5 fit interval) but
         /// it is not practical to differentiate between the first and
         ///
         /// second set following an upload when only looking at a 
         /// single set of elements.
         ///
         /// The rationalize( ) function is a means of addressing these 
         /// shortcomings.   The intention is to load all the navigation
         /// message data in the store, then call rationalize( ).  The
         /// function will sweep through the ordered set of elements and
         /// make appropriate adjustments to beginning and end of 
         /// validity values.  In general, the only changes will
         /// occur in set of elements immediately before an upload,
         /// the first set following the upload, and (perhaps) the
         /// second set following the upload. 
         ///
      void rationalize( );

      virtual ~OrbElemStore();

         /// Returns the position, velocity, and clock offset of the indicated
         /// object in ECEF coordinates (meters) at the indicated time.
         /// @param[in] id the object's identifier
         /// @param[in] t the time to look up
         /// @return the Xvt of the object at the indicated time
         /// @throw InvalidRequest If the request can not be completed for any
         ///    reason, this is thrown. The text may have additional
         ///    information as to why the request failed.
      virtual Xvt getXvt(const SatID& id, const CommonTime& t) const;

         /** Compute the position, velocity and clock offset of the
          * indicated object in ECEF coordinates (meters) at the
          * indicated time.
          * This method functions similarly to getXvt() except that it
          * does not throw an exception for any reason.  Instead, the
          * caller is expected to check the value of the "health"
          * field of the returned Xvt and decide what to do with the
          * data.
          * @note This function ignores the onlyHealthy flag.  It is
          *   up to the caller to examine the state of the health flag
          *   and decide what to do.
          * @param[in] id the object's identifier
          * @param[in] t the time to look up
          * @return the Xvt of the object at the indicated time */
      virtual Xvt computeXvt(const SatID& id, const CommonTime& t) const
         throw();

         /** Get the satellite health at a specific time.
          * @param[in] id the object's identifier
          * @param[in] t the time to look up
          * @return the health status of the object at the indicated time. */
      virtual Xvt::HealthStatus getSVHealth(const SatID& id,
                                            const CommonTime& t) const throw();

         /// A debugging function that outputs in human readable form,
         /// all data stored in this object.
         /// @param[in] s the stream to receive the output; defaults to cout
         /// @param[in] detail the level of detail to provide
      virtual void dump(std::ostream& s = std::cout, short detail = 0)
         const throw();

         /**
          * @throw InvalidParameter
          * @throw Exception
          */
      virtual bool addOrbElem(const OrbElemBase* eph);

         /// Edit the dataset, removing data outside the indicated time interval
         /// @param[in] tmin defines the beginning of the time interval
         /// @param[in] tmax defines the end of the time interval
      virtual void edit(const CommonTime& tmin, 
                        const CommonTime& tmax = CommonTime::END_OF_TIME)
         throw(); 

         /// Clear the dataset, meaning remove all data
      virtual void clear(void) throw();

         /// Determine the earliest time for which this object can successfully 
         /// determine the Xvt for any object.
         /// @return The initial time
         /// @throw InvalidRequest This is thrown if the object has no data.
      virtual CommonTime getInitialTime() const
         throw()
      { return initialTime; }


         /// Determine the latest time for which this object can successfully 
         /// determine the Xvt for any object.
         /// @return The final time
         /// @throw InvalidRequest This is thrown if the object has no data.
      virtual CommonTime getFinalTime() const
         throw()
      { return finalTime; }

         /// Return the number of orbit/clock elements stored in this store. 
      virtual unsigned size() const
         throw(); 

      virtual bool velocityIsPresent()
         const throw()
      { return true; }

         /// Return true if velocity data is present in the store
      virtual bool hasVelocity() const throw()
      { return true; }

         /// Return true if the given SatID is present in the store
      virtual bool isPresent(const SatID& id) const throw();

         /// Classes to set/access the store TimeSystem information.
      TimeSystem getTimeSystem() const { return timeSysForStore; }
      void setTimeSystem(const TimeSystem ts) { timeSysForStore = ts; }

         /// Classes to set/test the satellite system list.
      bool isSatSysPresent(const SatelliteSystem ss) const;
      void addSatSys(const SatelliteSystem ss); 
      void validSatSystem(const SatID& sat) const;
      std::list<SatelliteSystem> getValidSystemList() const;
      void dumpValidSystemList(std::ostream& out) const;  

      /*
       *  Explanation of find( ) function for OrbElemStore
       *  
       *  The findOrbElem( ) funtion
       *  does the best possible job of emulating the choice
       *  that would be made by a real-time user
       *
       *  It is strongly suggested that the user load ALL 
       *  available set of orbital elements into the store, 
       *  regardless of health status.  
       */
      /// @param sat SatID of satellite of interest
      /// @param t time with which to search for OrbElemBase
      /// @return a reference to the desired OrbElemBase
      /// @throw InvalidRequest object thrown when no OrbElemBase is found
      const OrbElemBase* findOrbElem( const SatID& sat, const CommonTime& t )
         const;

         /// Find an OrbElemBase for the indicated satellite at time
         /// t. The OrbElemBase chosen is the one with HOW time
         /// closest to the time t, (i.e. with smallest fabs(t-HOW),
         /// but still within the fit interval.
         /// @param sat the SV of interest
         /// @param t the time of interest
         /// @return a reference to desired OrbElemBase
         /// @throw InvalidRequest object thrown when no OrbElemBase is found
      const OrbElemBase* findNearOrbElem(const SatID& sat, const CommonTime& t)
         const;

         /// Find an OrbElemBase for the indicated satellite that has
         /// a Toe corresponding to time t.  If no such OrbElemBase
         /// exists in the store, throw an InvalidRequest exception
         /// @param sat the SV of interest
         /// @param t the time of interest
         /// @return a reference to desired OrbElemBase
         /// @throw InvalidRequest object thrown when no OrbElemBase is found
      const OrbElemBase* findToe(const SatID& sat, const CommonTime& t)
         const;

         /// Return a list of SatID object representing the satellites that
         /// are contained in the store.
         /// @return list of SatID objects
      std::list<gpstk::SatID> getSatIDList() const;

      virtual std::set<SatID> getIndexSet() const; 

         /// Add all ephemerides to an existing list<OrbElemBase>.
         /// @return the number of ephemerides added.
      int addToList( std::list<OrbElemBase*>& v ) const
         throw();

         /// This is intended to store sets of unique orbital elements
         /// for a single SV.  The key is the beginning of the period
         /// of validity for each set of elements.
      typedef std::map<CommonTime, OrbElemBase*> OrbElemMap;

         /** Returns a map of the ephemerides available for the specified
          * satellite.  Note that the return is specifically chosen as a
          * const reference.  The intent is to provide "read only" access
          * for analysis.  If the map needs to be modified, see other methods.
          * @throw InvalidRequest
          */
      const OrbElemMap& getOrbElemMap( const SatID& sat ) const;

   protected:
     
         /// This is intended to hold all unique OrbElemBase objects for each SV
         /// The key is the prn of the SV.
      typedef std::map<SatID, OrbElemMap> UBEMap;

         /// The map where all EngEphemerides are stored.
      UBEMap ube;

      CommonTime initialTime; //< Time of the first OrbElemBase in the store
      CommonTime finalTime;   //< Time of the last OrbElemBase in the store

         // List of the satellite systems stored in this store.  Typically
         // only one and set by descendents. 
      std::list<SatelliteSystem> sysList;
      
         // TimeSystem used in this store.  Set by default to "Any", but
         // typically overridden by descendents. 
      TimeSystem timeSysForStore; 

         // Here is a method to simplify the .cpp
      void updateInitialFinal(const OrbElemBase* eph)
      {
         if (eph->beginValid<initialTime)       
            initialTime = eph->beginValid;
         
         if (eph->endValid>finalTime)               
            finalTime = eph->endValid;
      }

   }; // end class

      //@}

} // namespace

#endif
